package com.fxu.framework.core.lock;

import com.fxu.framework.core.exception.MsgException;
import com.fxu.framework.core.holder.SpringHolder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/***
 * <p>xfs</p>
 * redisson实现分布式锁
 * @author fangxu
 * @version 1.0.0
 ***/
@Slf4j
@Component("fxuLockHolder")
@RequiredArgsConstructor
public class LockHolder {
	private final RedissonClient redissonClient;

	private static LockHolder lockHolder = null;
	public synchronized static LockHolder getLockHolder() {
		if (lockHolder == null) {
			lockHolder = SpringHolder.getBean("fxuLockHolder");
		}
		return lockHolder;
	}

	@FunctionalInterface
	public interface BizProcess<T> {
		/**
		 * Gets a result.
		 * @return a result
		 * @throws Exception 异常
		 */
		T process() throws Exception;
	}

	/**
	 * 使用redis分布式锁
	 * @param key 锁的key值
	 * @param second 获得锁时间
	 * @param bizProcess 执行动作
	 * @param <T> 执行动作范型类型
	 * @return 执行动作返回
	 */
	public <T> T lock(String key, int second, BizProcess<T> bizProcess) {
		key = "Redisson.Key:" + key;
		log.debug("lock start!key={}", key);
		RLock lock = redissonClient.getLock(key);
		try {
			lock.tryLock(second, TimeUnit.SECONDS);
			return bizProcess.process();
		} catch (Exception e) {
			log.error("lock error!", e);
			throw new MsgException(e.getMessage());
		} finally {
			if (lock.isLocked() && lock.isHeldByCurrentThread()) {
				log.debug("lock ended!key={}", key);
				lock.unlock();
			}
		}
	}

	/**
	 * 使用redis分布式锁
	 * @param key 锁的key值
	 * @param msg 获得锁异常提示
	 * @param bizProcess 执行动作
	 * @param <T> 执行动作范型类型
	 * @return 执行动作返回
	 */
	public <T> T lock(String key, String msg, BizProcess<T> bizProcess) {
		key = "Redisson.Key:" + key;
		log.debug("lock start!key={}", key);
		RLock lock = redissonClient.getLock(key);
		try {
			lock.tryLock(10, TimeUnit.SECONDS);
			return bizProcess.process();
		} catch (Exception e) {
			log.error("加锁执行异常", e);
			throw new MsgException(msg);
		} finally {
			if (lock.isLocked() && lock.isHeldByCurrentThread()) {
				log.debug("lock ended!key={}", key);
				lock.unlock();
			}
		}
	}
}
